/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "sec_storage_ipc_stub_serialization.h"

#include <securec.h>
#include <stddef.h>

#include "logger.h"
#include "module_common_ipc_stub.h"
#include "parcel.h"
#include "se_module_sec_storage_defines.h"
#include "sec_storage_ipc_defines.h"
#include "sec_storage_ipc_stub.h"

#ifdef ENABLE_FACTORY
ResultCode SetFactoryResetAuthKeyInputFromBuffer(const SharedDataBuffer *buffer, FactoryResetLevel *level,
    FactoryResetAuthAlgo *algo, StorageAuthKey *key)
{
    if (buffer == NULL) {
        LOG_ERROR("input buffer is invalid");
        return INVALID_PARA_NULL_PTR;
    }

    if (level == NULL || algo == NULL || key == NULL) {
        LOG_ERROR("input para is nullptr");
        return INVALID_PARA_NULL_PTR;
    }

    ResultCode ret = MEM_COPY_ERR;
    Parcel parcel = CreateParcelWithData(buffer->data, buffer->dataSize);
    do {
        if (!ParcelReadUint32(&parcel, level)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_1;
            LOG_ERROR("read level error");
            break;
        }

        if (!ParcelReadUint32(&parcel, algo)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_2;
            LOG_ERROR("read algo error");
            break;
        }

        uint32_t size = 0;
        if (!ParcelReadUint32(&parcel, &size)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_3;
            LOG_ERROR("read keySize error");
            break;
        }

        if (size > key->keySize) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_4;
            LOG_ERROR("read keyData error, size is %u", size);
            break;
        }

        if (!ParcelRead(&parcel, key->keyData, size)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_4;
            LOG_ERROR("read keyData error, size is %u", size);
            break;
        }

        key->keySize = size;
        ret = SUCCESS;
    } while (0);

    DeleteParcel(&parcel);
    return ret;
}

ResultCode SetToUserModeInputFromBuffer(const SharedDataBuffer *buffer, StorageUserModeConf *config)
{
    if (buffer == NULL) {
        LOG_ERROR("input buffer is invalid");
        return INVALID_PARA_NULL_PTR;
    }

    if (config == NULL) {
        LOG_ERROR("input config is nullptr");
        return INVALID_PARA_NULL_PTR;
    }

    if (buffer->dataSize != sizeof(StorageUserModeConf)) {
        LOG_ERROR("input size %u is not right", buffer->dataSize);
        return INVALID_PARA_ERR_SIZE;
    }

    *config = *(StorageUserModeConf *)buffer->data;
    return SUCCESS;
}

ResultCode SetSetAllSlotsSizeInputFromBuffer(const SharedDataBuffer *buffer, uint16_t *slotSizeArray,
    uint32_t *arrayLength)
{
    if (buffer == NULL) {
        LOG_ERROR("input buffer is invalid");
        return INVALID_PARA_NULL_PTR;
    }

    if (slotSizeArray == NULL || arrayLength == NULL) {
        LOG_ERROR("input slotSizeArray or arrayLength is nullptr");
        return INVALID_PARA_NULL_PTR;
    }

    ResultCode ret = IPC_RES_DATA_ERR;
    Parcel parcel = CreateParcelWithData(buffer->data, buffer->dataSize);
    do {
        uint32_t count = 0;
        if (!ParcelReadUint32(&parcel, &count)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_1;
            LOG_ERROR("read size data error");
            break;
        }

        if (count > MAX_SLOTS_NUM) {
            ret = IPC_PARA_PROCESS_ERR_INVALID_VALUE;
            LOG_ERROR("read size data is too big = %u", count);
            break;
        }

        if (count > *arrayLength) {
            ret = IPC_PARA_PROCESS_ERR_INVALID_VALUE;
            LOG_ERROR("input buffer is too small(%u > %u)", count, *arrayLength);
            break;
        }

        uint32_t readSize = count * sizeof(uint16_t);
        if (!ParcelRead(&parcel, (uint8_t *)slotSizeArray, readSize)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_2;
            LOG_ERROR("read slotSizeArray data error, size = %u", readSize);
            break;
        }

        *arrayLength = count;

        ret = SUCCESS;
    } while (0);

    DeleteParcel(&parcel);
    return ret;
}
#endif

ResultCode GetFactoryResetAuthKeyAlgoInputFromBuffer(const SharedDataBuffer *buffer, FactoryResetLevel *level)
{
    if (buffer == NULL) {
        LOG_ERROR("input buffer is invalid");
        return INVALID_PARA_NULL_PTR;
    }

    if (level == NULL) {
        LOG_ERROR("input level is nullptr");
        return INVALID_PARA_NULL_PTR;
    }
    if (buffer->dataSize != sizeof(uint32_t)) {
        LOG_ERROR("input buffer size is error = %u", buffer->dataSize);
        return INVALID_PARA_ERR_SIZE;
    }

    *level = *(FactoryResetLevel *)buffer->data;
    return SUCCESS;
}

ResultCode GetFactoryResetAuthKeyAlgoOutputToBuffer(FactoryResetAuthAlgo algo, SharedDataBuffer *buffer)
{
    if (buffer == NULL) {
        LOG_ERROR("input buffer is invalid");
        return INVALID_PARA_NULL_PTR;
    }

    if (memcpy_s(buffer->data, buffer->dataMaxSize, (uint8_t *)&algo, sizeof(FactoryResetAuthAlgo)) != EOK) {
        LOG_ERROR("input buffer size is invalid");
        return INVALID_PARA_ERR_SIZE;
    }

    buffer->dataSize = sizeof(FactoryResetAuthAlgo);
    return SUCCESS;
}

ResultCode PrepareFactoryResetOutputToBuffer(const uint8_t *nonce, uint32_t length, SharedDataBuffer *buffer)
{
    if (nonce == NULL || length == 0) {
        LOG_ERROR("input nonce is nullptr");
        return INVALID_PARA_NULL_PTR;
    }

    if (buffer == NULL) {
        LOG_ERROR("input buffer is invalid");
        return INVALID_PARA_NULL_PTR;
    }

    ResultCode ret = MEM_COPY_ERR;
    Parcel parcel = CreateParcel(buffer->dataMaxSize);
    do {
        if (!ParcelWriteUint32(&parcel, length)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_1;
            LOG_ERROR("write length error");
            break;
        }

        if (!ParcelWrite(&parcel, nonce, length)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_2;
            LOG_ERROR("write nonce error");
            break;
        }

        if (!ParcelToSharedDataBuffer(&parcel, buffer)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_3;
            LOG_ERROR("write buffer error");
            break;
        }

        ret = SUCCESS;
    } while (0);

    DeleteParcel(&parcel);
    return ret;
}

ResultCode ProcessFactoryResetInputFromBuffer(const SharedDataBuffer *buffer, FactoryResetLevel *level,
    uint8_t *credential, uint32_t *length)
{
    if (buffer == NULL) {
        LOG_ERROR("input buffer is invalid");
        return INVALID_PARA_NULL_PTR;
    }

    if (level == NULL || credential == NULL || length == NULL) {
        LOG_ERROR("input para is nullptr");
        return INVALID_PARA_NULL_PTR;
    }

    ResultCode ret = MEM_COPY_ERR;
    Parcel parcel = CreateParcelWithData(buffer->data, buffer->dataSize);
    do {
        if (!ParcelReadUint32(&parcel, level)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_1;
            LOG_ERROR("read level error");
            break;
        }

        uint32_t count = 0;
        if (!ParcelReadUint32(&parcel, &count)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_2;
            LOG_ERROR("read algo error");
            break;
        }

        if (count > *length) {
            ret = IPC_PARA_PROCESS_ERR_INVALID_VALUE;
            LOG_ERROR("input buffer is too small(%u > %u)", count, *length);
            break;
        }

        if (!ParcelRead(&parcel, credential, count)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_3;
            LOG_ERROR("read credential data error");
            break;
        }

        *length = count;
        ret = SUCCESS;
    } while (0);

    DeleteParcel(&parcel);
    return ret;
}

ResultCode GetSlotOperateAlgorithmInputFromBuffer(const SharedDataBuffer *buffer, SlotOperAlgo *algo)
{
    if (buffer == NULL) {
        LOG_ERROR("input buffer is invalid");
        return INVALID_PARA_NULL_PTR;
    }

    if (algo == NULL) {
        LOG_ERROR("input algo is nullptr");
        return INVALID_PARA_NULL_PTR;
    }

    if (buffer->dataSize != sizeof(SlotOperAlgo)) {
        LOG_ERROR("input size %u is not right", buffer->dataSize);
        return INVALID_PARA_ERR_SIZE;
    }

    *algo = *(SlotOperAlgo *)buffer->data;
    return SUCCESS;
}

ResultCode GetSlotOperateAlgorithmOutputToBuffer(uint32_t available, SharedDataBuffer *buffer)
{
    if (buffer == NULL) {
        LOG_ERROR("input buffer is invalid");
        return INVALID_PARA_NULL_PTR;
    }

    if (buffer->dataMaxSize < sizeof(uint32_t)) {
        return INVALID_PARA_ERR_SIZE;
    }

    *(uint32_t *)buffer->data = available;
    buffer->dataSize = sizeof(uint32_t);
    return SUCCESS;
}

ResultCode GetFactoryResetAlgorithmInputFromBuffer(const SharedDataBuffer *buffer, FactoryResetAuthAlgo *algo)
{
    if (buffer == NULL) {
        LOG_ERROR("input buffer is invalid");
        return INVALID_PARA_NULL_PTR;
    }

    if (algo == NULL) {
        LOG_ERROR("input algo is nullptr");
        return INVALID_PARA_NULL_PTR;
    }

    if (buffer->dataSize != sizeof(FactoryResetAuthAlgo)) {
        LOG_ERROR("input size %u is not right", buffer->dataSize);
        return INVALID_PARA_ERR_SIZE;
    }

    *algo = *(FactoryResetAuthAlgo *)buffer->data;
    return SUCCESS;
}

ResultCode GetFactoryResetAlgorithmOutputToBuffer(uint32_t available, SharedDataBuffer *buffer)
{
    if (buffer == NULL) {
        LOG_ERROR("input buffer is invalid");
        return INVALID_PARA_NULL_PTR;
    }

    if (buffer->dataMaxSize < sizeof(uint32_t)) {
        return INVALID_PARA_ERR_SIZE;
    }

    *(uint32_t *)buffer->data = available;
    buffer->dataSize = sizeof(uint32_t);
    return SUCCESS;
}

ResultCode GetAllSlotsSizeOutputToBuffer(const uint16_t *slotSizeArray, uint32_t arrayLength, SharedDataBuffer *buffer)
{
    if (slotSizeArray == NULL) {
        LOG_ERROR("input slotSizeArray is nullptr");
        return INVALID_PARA_NULL_PTR;
    }

    if (arrayLength > MAX_SLOTS_NUM || arrayLength == 0) {
        LOG_ERROR("input slotSizeArray is err = %u", arrayLength);
        return INVALID_PARA_NULL_PTR;
    }

    if (buffer == NULL) {
        LOG_ERROR("input buffer is invalid");
        return INVALID_PARA_NULL_PTR;
    }

    ResultCode ret = MEM_WRITE_ERR;
    Parcel parcel = CreateParcel(sizeof(uint32_t) + arrayLength * sizeof(uint16_t));
    do {
        if (!ParcelWriteUint32(&parcel, arrayLength)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_1;
            LOG_ERROR("write arrayLength error");
            break;
        }

        if (!ParcelWrite(&parcel, (const uint8_t *)slotSizeArray, arrayLength * sizeof(uint16_t))) {
            LOG_ERROR("write slotSizeArray failed");
            ret = IPC_PARA_PROCESS_ERR_INDEX_2;
            break;
        }

        if (!ParcelToSharedDataBuffer(&parcel, buffer)) {
            ret = IPC_PARA_PROCESS_ERR_INVALID_VALUE;
            LOG_ERROR("ParcelToDataBuffer keyData error");
            break;
        }
        ret = SUCCESS;
    } while (0);

    DeleteParcel(&parcel);
    return ret;
}

ResultCode AllocateSlotInputFromBuffer(const SharedDataBuffer *buffer, StorageFileName *name, StorageSlotAttr *slotAttr,
    StorageAuthKey *slotKey)
{
    if (buffer == NULL) {
        LOG_ERROR("input buffer is invalid");
        return INVALID_PARA_NULL_PTR;
    }

    if (name == NULL || slotAttr == NULL || slotKey == NULL) {
        LOG_ERROR("input para is nullptr");
        return INVALID_PARA_NULL_PTR;
    }

    ResultCode ret = IPC_RES_DATA_ERR;
    Parcel parcel = CreateParcelWithData(buffer->data, buffer->dataSize);
    do {
        if (!ParcelRead(&parcel, (uint8_t *)name, sizeof(StorageFileName))) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_1;
            LOG_ERROR("read StorageFileName failed");
            break;
        }

        if (!ParcelRead(&parcel, (uint8_t *)slotAttr, sizeof(StorageSlotAttr))) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_2;
            LOG_ERROR("read StorageSlotAttr failed");
            break;
        }

        uint32_t size = 0;
        if (!ParcelReadUint32(&parcel, &size)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_3;
            LOG_ERROR("read keySize error");
            break;
        }

        if (size > slotKey->keySize) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_4;
            LOG_ERROR("read keySize error = %u", size);
            break;
        }

        if (!ParcelRead(&parcel, slotKey->keyData, size)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_4;
            LOG_ERROR("read keyData error, size is %u", size);
            break;
        }

        slotKey->keySize = size;

        ret = SUCCESS;
    } while (0);

    DeleteParcel(&parcel);

    return ret;
}

ResultCode WriteSlotInputFromBuffer(const SharedDataBuffer *buffer, StorageFileName *name, StorageAuthKey *key,
    StorageDataArea *area, StorageDataBuffer *data)
{
    if (buffer == NULL) {
        LOG_ERROR("input buffer is invalid");
        return INVALID_PARA_NULL_PTR;
    }

    if (name == NULL || key == NULL) {
        LOG_ERROR("input para name or key is nullptr");
        return INVALID_PARA_NULL_PTR;
    }

    if (area == NULL || data == NULL) {
        LOG_ERROR("input para area or data is nullptr");
        return INVALID_PARA_NULL_PTR;
    }

    ResultCode ret = IPC_RES_DATA_ERR;
    Parcel parcel = CreateParcelWithData(buffer->data, buffer->dataSize);
    do {
        if (!ParcelReadStorageIndicator(&parcel, name, key, area)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_1;
            LOG_ERROR("read indicator error");
            break;
        }

        uint32_t size = 0;
        if (!ParcelReadUint32(&parcel, &size)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_2;
            LOG_ERROR("read keySize error");
            break;
        }

        if (size > data->bufferSize) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_3;
            LOG_ERROR("read buffer error, size is %u", size);
            break;
        }

        if (!ParcelRead(&parcel, (uint8_t *)data->bufferData, size)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_3;
            LOG_ERROR("read buffer error, size is %u", size);
            break;
        }

        data->bufferSize = size;

        ret = SUCCESS;
    } while (0);

    DeleteParcel(&parcel);
    return ret;
}

ResultCode ReadSlotInputFromBuffer(const SharedDataBuffer *buffer, StorageFileName *name, StorageAuthKey *key,
    StorageDataArea *area)
{
    if (buffer == NULL) {
        LOG_ERROR("input buffer is invalid");
        return INVALID_PARA_NULL_PTR;
    }
    if (name == NULL || key == NULL || area == NULL) {
        LOG_ERROR("input buffer is invalid");
        return INVALID_PARA_NULL_PTR;
    }

    ResultCode ret = IPC_RES_DATA_ERR;
    Parcel parcel = CreateParcelWithData(buffer->data, buffer->dataSize);
    do {
        if (!ParcelReadStorageIndicator(&parcel, name, key, area)) {
            ret = IPC_PARA_PROCESS_ERR_INVALID_VALUE;
            LOG_ERROR("read indicator error");
            break;
        }
        ret = SUCCESS;
    } while (0);

    DeleteParcel(&parcel);
    return ret;
}

ResultCode ReadSlotOutputToBuffer(const StorageDataBuffer *data, SharedDataBuffer *buffer)
{
    if (data == NULL) {
        return INVALID_PARA_NULL_PTR;
    }

    if (buffer == NULL) {
        LOG_ERROR("input buffer is invalid");
        return INVALID_PARA_NULL_PTR;
    }

    if (memcpy_s(buffer->data, buffer->dataMaxSize, data->bufferData, data->bufferSize) != EOK) {
        LOG_ERROR("memcpy_s failed");
        return MEM_COPY_ERR;
    }

    buffer->dataSize = data->bufferSize;
    return SUCCESS;
}

ResultCode FreeSlotInputFromBuffer(const SharedDataBuffer *buffer, StorageFileName *name, StorageAuthKey *key)
{
    if (buffer == NULL) {
        LOG_ERROR("input buffer is invalid");
        return INVALID_PARA_NULL_PTR;
    }
    if (name == NULL || key == NULL) {
        LOG_ERROR("input name or key is invalid");
        return INVALID_PARA_NULL_PTR;
    }

    ResultCode ret = IPC_RES_DATA_ERR;
    Parcel parcel = CreateParcelWithData(buffer->data, buffer->dataSize);
    do {
        if (!ParcelRead(&parcel, (uint8_t *)name, sizeof(StorageFileName))) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_1;
            LOG_ERROR("read StorageFileName failed");
            break;
        }

        uint32_t size = 0;
        if (!ParcelReadUint32(&parcel, &size)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_2;
            LOG_ERROR("read keySize error");
            break;
        }

        if (size > key->keySize) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_3;
            LOG_ERROR("read keyData error, size is %u", size);
            break;
        }

        if (!ParcelRead(&parcel, key->keyData, size)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_3;
            LOG_ERROR("read keyData error, size is %u", size);
            break;
        }

        key->keySize = size;
        ret = SUCCESS;
    } while (0);

    DeleteParcel(&parcel);

    return ret;
}

ResultCode GetSlotStatusInputFromBuffer(const SharedDataBuffer *buffer, StorageFileName *name)
{
    if (buffer == NULL || name == NULL) {
        LOG_ERROR("input buffer is invalid");
        return INVALID_PARA_NULL_PTR;
    }

    if (memcpy_s(name, sizeof(StorageFileName), buffer->data, buffer->dataSize) != EOK) {
        LOG_ERROR("memcpy_s error");
        return MEM_COPY_ERR;
    }

    return SUCCESS;
}

ResultCode GetSlotStatusOutputToBuffer(const StorageSlotStatus *status, SharedDataBuffer *buffer)
{
    if (status == NULL || buffer == NULL) {
        LOG_ERROR("input buffer is invalid");
        return INVALID_PARA_NULL_PTR;
    }

    if (memcpy_s(buffer->data, buffer->dataMaxSize, status, sizeof(StorageSlotStatus)) != EOK) {
        LOG_ERROR("memcpy_s error");
        return MEM_COPY_ERR;
    }

    buffer->dataSize = sizeof(StorageSlotStatus);
    return SUCCESS;
}

// private impl
bool ParcelReadStorageIndicator(Parcel *parcel, StorageFileName *name, StorageAuthKey *key, StorageDataArea *area)
{
    if (parcel == NULL) {
        LOG_ERROR("input para parcel or indicator nullptr");
        return false;
    }

    if (name == NULL || key == NULL || area == NULL) {
        LOG_ERROR("input para name or key or area is nullptr");
        return false;
    }

    if (!ParcelRead(parcel, (uint8_t *)name, sizeof(StorageFileName))) {
        LOG_ERROR("read StorageFileName failed");
        return false;
    }

    uint32_t size = 0;
    if (!ParcelReadUint32(parcel, &size)) {
        LOG_ERROR("read keySize error");
        return false;
    }
    if (size > key->keySize) {
        LOG_ERROR("read keySize error, size is %u", size);
        return false;
    }

    if (!ParcelRead(parcel, key->keyData, size)) {
        LOG_ERROR("read keyData error, size is %u", size);
        return false;
    }

    key->keySize = size;

    if (!ParcelRead(parcel, (uint8_t *)area, sizeof(StorageDataArea))) {
        LOG_ERROR("read area error");
        return false;
    }
    return true;
}

bool ParcelToSharedDataBuffer(const Parcel *parcel, SharedDataBuffer *buffer)
{
    if (parcel == NULL) {
        LOG_ERROR("input parcel is nullptr");
        return false;
    }

    if (buffer == NULL) {
        LOG_ERROR("input buffer is invalid");
        return false;
    }

    uint32_t size = buffer->dataMaxSize;

    if (!ParcelToDataBuffer(parcel, buffer->data, &size)) {
        LOG_ERROR("buffer size is not enough, %u < %u", buffer->dataMaxSize, size);
        return false;
    }

    buffer->dataSize = size;
    return true;
}
